#include "SRObjModelLoader.h"
#include "SRObjModel.h"
#include "SRData.h"
#include <cstdlib>
#include <cassert>

using std::string;
using std::vector;
using matsu::vec3;
using matsu::vec2;
using matsu::vec4;
using std::auto_ptr;
using std::map;

namespace sora
{
	ObjModelParser::ObjModelParser()
	{
	}
	ObjModelParser &ObjModelParser::parser()
	{
		static ObjModelParser p;
		return p;
	}
	vector<string> ObjModelParser::split(const string &line) const
	{
		//line앞뒤로 공백문자는 전부 없앤다. 아래의 로직은 처리결과를 사용해서 처리한다
		string lineStr = mio::str::trim(line);
		if(lineStr.length() == 0 || lineStr[0] == '#')
		{
			//비었거나 주석이면 빈거 반환
			return vector<string>();
		}
		//공백으로 짤라진 결과물
		vector<string> spaceTokenList = mio::str::split(line, " \t\r\n");

		vector<string> tokenList;
		//TODO: '1/3', '1//3'은 띄어쓰기가 없지만 각각 3개의 token으로 구성되어있다. 이것도 적절히 파싱하자
		for(std::size_t i = 0 ; i < spaceTokenList.size() ; i++)
		{
			const string &str = spaceTokenList[i];
			assert(str.find_first_of("\\") == string::npos);	// '\'는 일단 배제(이거있으면 파서가 더 복잡해져야될듯
			int n = str.find_first_of("/");
			if(n == string::npos)
			{
				// '/'이 없는 경우는 그냥 통짜 토근
				tokenList.push_back(str);
				continue;
			}
			//'/'과 아닌것을 기준으로 나눈다

			if(str[n+1] == '/')
			{
				//'//'인 경우
				//2개인 경우
				string t1 = str.substr(0, n);
				string t2 = "//";
				string t3 = str.substr(n+2, str.length());
				tokenList.push_back(t1);
				tokenList.push_back(t2);
				tokenList.push_back(t3);
			}
			else
			{
				int start = str.find_first_of("/");
				int end = str.find_last_of("/");
				if(start == end)
				{
					//1개인 경우
					string t1 = str.substr(0, n);
					string t2 = "/";
					string t3 = str.substr(n+1, str.length());
					tokenList.push_back(t1);
					tokenList.push_back(t2);
					tokenList.push_back(t3);
				}
				else
				{
					//'/'가 2개
					string split = "/";
					string t1 = str.substr(0, start);
					string t2 = str.substr(start+1, end-start-1);
					string t3 = str.substr(end+1, str.length());
					tokenList.push_back(t1);
					tokenList.push_back(split);
					tokenList.push_back(t2);
					tokenList.push_back(split);
					tokenList.push_back(t3);
				}
			}

		}
		return tokenList;

	}

#if _IPHONE_
#pragma mark reader
#endif
	ObjModelReader::ObjModelReader(const std::string &file)
	{
		reader_ = auto_ptr<mio::BufferFileReader>(new mio::BufferFileReader(file));
	}
	std::string ObjModelReader::readLine()
	{
		string line;
		// '\'로 끝나는 경우 다음줄까지 붙여서 반환
		if(reader_->canRead() == true)
		{
			line = reader_->readLine();
			if(line.size() >= 1)
				SR_ASSERT(line[line.length()-1] != '\\');	//2줄잇이는 아직 구현안함
		}
		return line;
	}
	bool ObjModelReader::canRead()
	{
		return reader_->canRead();
	}

#if _IPHONE_
#pragma mark obj model loader
#endif
	ObjModelLoader::ObjModelLoader()
	{
	}
	ObjNodeFSM &ObjModelLoader::fsm() const
	{
		if(fsm_.get() == NULL)
		{
			fsm_ = auto_ptr<ObjNodeFSM>(new ObjNodeFSM());
		}
		return *fsm_;
	}

	void ObjModelLoader::getModelNodeData(const std::vector<std::string> &lines, std::vector<ObjNodeData*> &nodelist) const
	{
		vector<string>::const_iterator iter;
		for(iter = lines.begin() ; iter != lines.end() ; iter++)
		{
			vector<string> tokenStrList = ObjModelParser::parser().split(*iter);
			if(tokenStrList.size() == 0)
				continue;

			ObjNodeData *nodedata = new ObjNodeData();
			ObjNodeState &state = fsm().state(tokenStrList[0]);
			state.parse(tokenStrList, *nodedata);

			nodelist.push_back(nodedata);
		}
	}
	Model *ObjModelLoader::load(const std::string &file) const
	{
		MeshModelBuilder builder;

		std::string path = mio::path::appPath(file);
		ObjModelReader reader(path);
		while(reader.canRead())
		{
			string line = reader.readLine();
			vector<string> tokens = ObjModelParser::parser().split(line);
			if(tokens.size() == 0)
				continue;

			//fsm의 상태 전이
			ObjNodeKeyword keyword = ObjModel::str2keyword(tokens[0]);
			fsm().transit(keyword);

			ObjNodeState &state = fsm().curr();
			ObjNodeData node;
			state.parse(tokens, node);
			state.update(node, builder);
		}

		fsm().flush(builder);
		//최종적으로 읽어진 데이터를 사용해서 model을 구성한다
		return builder.build();
	}

#if _IPHONE_
#pragma mark obj model elem fsm
#endif
	ObjNodeFSM::ObjNodeFSM()
	: curr_(NULL)
	{
		stateMap_[ObjNodeKeywordFace] = new ObjNodeFaceState(this);

		stateMap_[ObjNodeKeywordVertexNormal] = new ObjNodeVertexNormalState(this);
		stateMap_[ObjNodeKeywordGeometricVertex] = new ObjNodeGeometricVertexState(this);
		stateMap_[ObjNodeKeywordTextureVertex] = new ObjNodeTextureVertexState(this);
		stateMap_[ObjNodeKeywordParameterSpaceVertex] = new ObjNodeParameterSpaceVertexState(this);

		stateMap_[ObjNodeKeywordGroupName] = new ObjNodeGroupNameState(this);

		stateMap_[ObjNodeKeywordMaterialName] = new ObjNodeMaterialNameState(this);
		stateMap_[ObjNodeKeywordMaterialLibrary] = new ObjNodeMaterialLibraryState(this);
	}
	ObjNodeFSM::~ObjNodeFSM()
	{
		map<ObjNodeKeyword, ObjNodeState*>::iterator iter;
		for(iter = stateMap_.begin() ; iter != stateMap_.end() ; iter++)
		{
			ObjNodeState *state = iter->second;
			delete(state);
		}
	}
	ObjNodeState &ObjNodeFSM::curr() const
	{
		SR_ASSERT(curr_ != NULL);
		return *curr_;
	}
	bool ObjNodeFSM::isCurrExist() const
	{
		if(curr_ == NULL)
			return false;
		else
			return true;
	}
	void ObjNodeFSM::transit(ObjNodeKeyword keyword)
	{
		if(isCurrExist() == true)
		{
			ObjNodeState *next = curr().next(keyword);
			curr_ = next;
		}
		else
		{
			//현재 상태가 아직 지정되지 않은 경우
			curr_ = &state(keyword);
		}

	}
	ObjNodeState &ObjNodeFSM::state(const std::string &keywordStr) const
	{
		ObjNodeKeyword keyword = ObjModel::str2keyword(keywordStr);
		return state(keyword);
	}
	ObjNodeState &ObjNodeFSM::state(ObjNodeKeyword keyword) const
	{
		map<ObjNodeKeyword, ObjNodeState*>::const_iterator iter = stateMap_.find(keyword);
		SR_ASSERT(iter != stateMap_.end());
		ObjNodeState *state = iter->second;
		return *state;
	}
	void ObjNodeFSM::addPos(const matsu::vec4 &pos)
	{
		posList_.push_back(pos);
	}
	void ObjNodeFSM::addTexCoord(const matsu::vec2 &texCoord)
	{
		texCoordList_.push_back(texCoord);
	}
	void ObjNodeFSM::addNormal(const matsu::vec3 &normal)
	{
		normalList_.push_back(normal);
	}

	const matsu::vec4 &ObjNodeFSM::pos(int index) const
	{
		SR_ASSERT(index >= 1 && index <= static_cast<int>(posList_.size()));
		//elem에 기록된 인덱스(1부터 시작)을 그냥 가져올수 있음
		return posList_[index-1];
	}
	const matsu::vec2 &ObjNodeFSM::texCoord(int index) const
	{
		SR_ASSERT(index >= 1 && index <= static_cast<int>(texCoordList_.size()));
		return texCoordList_[index-1];
	}
	const matsu::vec3 &ObjNodeFSM::normal(int index) const
	{
		SR_ASSERT(index >= 1 && index <= static_cast<int>(normalList_.size()));
		return normalList_[index-1];
	}
	void ObjNodeFSM::flush(MeshModelBuilder &builder)
	{
		map<ObjNodeKeyword, ObjNodeState*>::iterator iter;
		for(iter = stateMap_.begin() ; iter != stateMap_.end() ; iter++)
		{
			ObjNodeState *state = iter->second;
			state->flush(builder);
		}
	}

#if _IPHONE_
#pragma mark obj model elem state
#endif
	ObjNodeFSM &ObjNodeState::fsm() const
	{
		SR_ASSERT(fsm_ != NULL);
		return *fsm_;
	}
	void ObjNodeState::parse(const std::string &line, ObjNodeData &data)
	{
		vector<string> tokens = ObjModelParser::parser().split(line);
		parse(tokens, data);
	}
	ObjNodeState *ObjNodeState::next(const ObjNodeData &data)
	{
		ObjNodeKeyword keyword = data.keyword();
		return next(keyword);
	}

	ObjNodeCategoryElementState::ObjNodeCategoryElementState(ObjNodeFSM *fsm)
	: ObjNodeState(fsm)
	{
	}
	void ObjNodeCategoryElementState::parse(const std::vector<std::string> &tokens, ObjNodeData &data)
	{
		//TODO: element node에서 공통파싱 부분을 뺴내기
	}
	void ObjNodeCategoryElementState::update(const ObjNodeData &data, MeshModelBuilder &builder)
	{
		//TODO:
	}
	ObjNodeState *ObjNodeCategoryElementState::next(ObjNodeKeyword keyword)
	{
		ObjNodeCategory category = ObjModel::str2category(keyword);

		if(category == ObjNodeCategoryElement
		   || category == ObjNodeCategoryGrouping)
		{
			ObjNodeState &state = fsm().state(keyword);
			return &state;
		}
		SR_ASSERT(!"not valid fsm");
		return NULL;
	}

	ObjNodeFaceState::ObjNodeFaceState(ObjNodeFSM *fsm)
	: ObjNodeCategoryElementState(fsm), prevUseTex_(false), prevUseNormal_(false)
	{
	}
	void ObjNodeFaceState::update(const ObjNodeData &data, MeshModelBuilder &builder)
	{
		//TODO: prev use tex, prev use normal을 사용해서 바뀌는 순간을 알아내고
		//그것단위로 묶어서 draw command만들 자료를 구성하자
		const int vertexCnt = data.numVertex();
		vector<ModelVertex3> vertexList;

		for(int i = 0 ; i < vertexCnt ; i++)
		{
			//index. 이것을 사용해서 FSM정보 찾기 + fsm에서 해당 인덱스의 정보 얻기
			int posIndex = data.vertex(i);
			vec3 pos(fsm().pos(posIndex).toVec3());

			vec2 texCoord;
			vec3 normal;

			if(data.useTex == true)
			{
				int texCoordIndex = data.tex(i);
				texCoord = fsm().texCoord(texCoordIndex);
			}
			if(data.useNormal == true)
			{
				int normalIndex = data.normal(i);
				normal = fsm().normal(normalIndex);
			}

			ModelVertex3 vertex(pos, texCoord, normal);
			vertexList.push_back(vertex);
		}

		//face는 triangle fan을 Triangles로 흉내내서 구현
		vector<GLushort> indexList;
		assert(vertexList.size() >= 3);
		for(std::size_t i = 0 ; i < vertexList.size() - 2 ; i++)
		{
			indexList.push_back(0);
			indexList.push_back(i+1);
			indexList.push_back(i+2);
		}

		if(prevUseTex_ == data.useTex && prevUseNormal_ == data.useNormal)
		{
			const int prevVertexListSize = vertexList_.size();
			//이전 노드와 현재 노드의 tex, normal 사용여부가 동일하면 서로 묶을수 있다
			for(std::size_t i = 0 ; i < vertexList.size() ; i++)
				vertexList_.push_back(vertexList[i]);
			for(std::size_t i = 0 ; i < indexList.size() ; i++)
				indexList_.push_back(indexList[i] + prevVertexListSize);
		}
		else
		{
			if(vertexList_.size() > 0)
			{
				//prev와 현재 노드의 tex, normal의 속성이 다르면 같은 그룹으로 묶는것이 불가능하다는 소리
				builder.addMesh(GL_TRIANGLES, vertexList_, indexList_, prevUseTex_, prevUseNormal_);

				//vertex, index 정보 폭파
				vertexList_.clear();
				indexList_.clear();
			}
			const int prevVertexListSize = vertexList_.size();
			//이전 노드와 현재 노드의 tex, normal 사용여부가 동일하면 서로 묶을수 있다
			for(std::size_t i = 0 ; i < vertexList.size() ; i++)
				vertexList_.push_back(vertexList[i]);
			for(std::size_t i = 0 ; i < indexList.size() ; i++)
				indexList_.push_back(indexList[i] + prevVertexListSize);
		}

		//model->add(GL_TRIANGLES, vertexList, indexList);	//TODO: 실제 모델에 적절힌 반영하기

		//prev use tex, prev use normal 갱신
		prevUseTex_ = data.useTex;
		prevUseNormal_ = data.useNormal;
	}
	void ObjNodeFaceState::flush(MeshModelBuilder &builder)
	{
		builder.addMesh(GL_TRIANGLES, vertexList_, indexList_, prevUseTex_, prevUseNormal_);

		//vertex, index 정보 폭파
		vertexList_.clear();
		indexList_.clear();
	}
	void ObjNodeFaceState::parse(const std::vector<std::string> &tokens, ObjNodeData &data)
	{
		data.reset();
		data.keyword_ = ObjNodeKeywordFace;
		checkVertexNormalTexUse(tokens, data.useTex, data.useNormal);

		//값 읽기
		if(data.useTex == false && data.useNormal == false)
		{
			//vertex
			for(std::size_t i = 1 ; i < tokens.size() ; i++)
			{
				const string &token = tokens[i];
				int num = atoi(token.c_str());
				assert(num != 0);
				data.intList_.push_back(num);
			}
		}
		else if(data.useTex == false && data.useNormal == true)
		{
			//vertex-normal
			assert((tokens.size()-1) % 3 == 0);
			int numVertex = (tokens.size() - 1) / 3;
			for(int i = 0 ; i < numVertex ; i++)
			{
				const string &vertexToken = tokens[1 + i*3];
				const string &normalToken = tokens[1 + i*3 + 2];
				int vertex = atoi(vertexToken.c_str());
				int normal = atoi(normalToken.c_str());
				assert(vertex != 0 && normal != 0);
				data.intList_.push_back(vertex);
				data.intList_.push_back(normal);
			}
		}
		else if(data.useTex == true && data.useNormal == false)
		{
			//vertex-tex
			assert((tokens.size()-1) % 3 == 0);
			int numVertex = (tokens.size() - 1) / 3;
			for(int i = 0 ; i < numVertex ; i++)
			{
				const string &vertexToken = tokens[1 + i*3];
				const string &texToken = tokens[1 + i*3 + 2];
				int vertex = atoi(vertexToken.c_str());
				int tex = atoi(texToken.c_str());
				assert(vertex != 0 && tex != 0);
				data.intList_.push_back(vertex);
				data.intList_.push_back(tex);
			}
		}
		else if(data.useTex == true && data.useNormal == true)
		{
			//vertex-tex-normal
			assert((tokens.size()-1) % 5 == 0);
			int numVertex = (tokens.size()-1) / 5;
			for(int i = 0 ; i < numVertex ; i++)
			{
				const string &vertexToken = tokens[1 + i*5];
				const string &texToken = tokens[1 + i*5+2];
				const string &normalToken = tokens[1 + i*5 + 4];
				int vertex = atoi(vertexToken.c_str());
				int tex = atoi(texToken.c_str());
				int normal = atoi(normalToken.c_str());
				assert(vertex != 0 && tex != 0 && normal != 0);
				data.intList_.push_back(vertex);
				data.intList_.push_back(tex);
				data.intList_.push_back(normal);
			}
		}
	}
	void ObjNodeFaceState::checkVertexNormalTexUse(const std::vector<std::string> &tokens, bool &useTex, bool &useNormal)
	{
		//구분자에 따른 분기
		//no -> vertex
		//'/' -> vertex/tex
		//'//' -> vertex/normal
		//'/'/' -> vertex/tex/normal

		//최초에 어떤 모드로 할지 결정했으면 나머지도 전부 동일해야한다
		//2 : '/' or '//'(vertex/normal) or 숫자(no)
		//4 : '/'(vertex/tex/normal) or 숫자(vertex/tex)
		const string &token2 = tokens[2];
		if(token2 == "//")
		{
			//vertex-normal
			useTex = false;
			useNormal = true;
		}
		else if(token2 == "/")
		{
			if(tokens.size() >= 5)
			{
				//tokens가 최소한 5개능 있어야 a/b/c테스트가 가능하다
				const string &token4 = tokens[4];
				if(token4 == "/")
				{
					//vertex-tex-normal
					useTex = true;
					useNormal = true;
				}
				else
				{
					//vertex-tex
					useTex = true;
					useNormal = false;
				}
			}
			else
			{
				//vertex-tex
				useTex = true;
				useNormal = false;
			}
		}
		else
		{
			useTex = false;
			useNormal = false;
		}
	}
#if _IPHONE_
#pragma mark ObjNodeCategoryVertexDataState
#endif
	ObjNodeCategoryVertexDataState::ObjNodeCategoryVertexDataState(ObjNodeFSM *fsm)
	: ObjNodeState(fsm)
	{
	}
	void ObjNodeCategoryVertexDataState::parse(const std::vector<std::string> &tokens, ObjNodeData &data)
	{
		data.reset();
		for(std::size_t i = 0 ; i < tokens.size()-1 ; i++)
		{
			const string &token = tokens[i+1];
			data.value[i] = static_cast<float>(atof(token.c_str()));
		}
		data.valueCnt = tokens.size()-1;
	}
	void ObjNodeCategoryVertexDataState::update(const ObjNodeData &data, MeshModelBuilder &builder)
	{
		//TODO
	}
	ObjNodeState *ObjNodeCategoryVertexDataState::next(ObjNodeKeyword keyword)
	{
		ObjNodeCategory category = ObjModel::str2category(keyword);

		if(category == ObjNodeCategoryVertexData
		   || category == ObjNodeCategoryElement
		   || category == ObjNodeCategoryGrouping)
		{
			ObjNodeState &state = fsm().state(keyword);
			return &state;
		}
		SR_ASSERT(!"not valid fsm");
		return NULL;
	}
#if _IPHONE_
#pragma mark ObjNodeGeometricVertexState
#endif
	ObjNodeGeometricVertexState::ObjNodeGeometricVertexState(ObjNodeFSM *fsm)
	: ObjNodeCategoryVertexDataState(fsm)
	{
	}
	void ObjNodeGeometricVertexState::parse(const std::vector<std::string> &tokens, ObjNodeData &data)
	{
		SR_ASSERT(tokens.size() == 3+1 || tokens.size() == 4+1);	//요수는 3~4개
		ObjNodeCategoryVertexDataState::parse(tokens, data);
		data.keyword_ = ObjNodeKeywordGeometricVertex;

		//3개만 설정된 경우 마지막은 기본값 설절
		if(data.valueCnt == 3)
			data.value[3] = 1;	//4번쨰 요소의 기본값=1
	}
	void ObjNodeGeometricVertexState::update(const ObjNodeData &data, MeshModelBuilder &builder)
	{
		vec4 pos(&data.value[0]);
		fsm().addPos(pos);
	}

#if _IPHONE_
#pragma mark ObjNodeTextureVertexState
#endif
	ObjNodeTextureVertexState::ObjNodeTextureVertexState(ObjNodeFSM *fsm)
	: ObjNodeCategoryVertexDataState(fsm)
	{
	}
	void ObjNodeTextureVertexState::update(const ObjNodeData &data, MeshModelBuilder &builder)
	{
		vec2 texCoord(&data.value[0]);
		fsm().addTexCoord(texCoord);
	}
	void ObjNodeTextureVertexState::parse(const std::vector<std::string> &tokens, ObjNodeData &data)
	{
		const int tokensize = tokens.size();
		SR_ASSERT(tokensize >= 2 && tokensize <= 4);	//인자는 1~3개
		ObjNodeCategoryVertexDataState::parse(tokens, data);
		data.keyword_ = ObjNodeKeywordTextureVertex;

		//기본값 설정
		if(data.valueCnt == 1)
		{
			data.value[1] = 0;
			data.value[2] = 0;
		}
		else if(data.valueCnt == 2)
			data.value[2] = 0;
	}

#if _IPHONE_
#pragma mark ObjNodeVertexNormalState
#endif
	ObjNodeVertexNormalState::ObjNodeVertexNormalState(ObjNodeFSM *fsm)
	: ObjNodeCategoryVertexDataState(fsm)
	{
	}
	void ObjNodeVertexNormalState::parse(const std::vector<std::string> &tokens, ObjNodeData &data)
	{
		SR_ASSERT(tokens.size() == 3+1);	//normal은 3개 요소 벡터
		ObjNodeCategoryVertexDataState::parse(tokens, data);
		data.keyword_ = ObjNodeKeywordVertexNormal;
	}
	void ObjNodeVertexNormalState::update(const ObjNodeData &data, MeshModelBuilder &builder)
	{
		vec3 normal(data.value[0], data.value[1], data.value[2]);
		fsm().addNormal(normal);
	}

#if _IPHONE_
#pragma mark ObjNodeParameterSpaceVertexState
#endif
	ObjNodeParameterSpaceVertexState::ObjNodeParameterSpaceVertexState(ObjNodeFSM *fsm)
	: ObjNodeCategoryVertexDataState(fsm)
	{
	}

	void ObjNodeParameterSpaceVertexState::parse(const std::vector<std::string> &tokens, ObjNodeData &data)
	{
		const int tokensize = tokens.size();
		SR_ASSERT(tokensize >= 2 && tokensize <= 4);	//인자는 1~3개
		ObjNodeCategoryVertexDataState::parse(tokens, data);
		data.keyword_ = ObjNodeKeywordParameterSpaceVertex;

		//기본값 설정
		if(data.valueCnt == 1)
		{
			data.value[1] = 1;
			data.value[2] = 1;
		}
		else if(data.valueCnt == 2)
		{
			data.value[2] = 1;
		}
	}

	ObjNodeCategoryDisplayRenderAttributeState::ObjNodeCategoryDisplayRenderAttributeState(ObjNodeFSM *fsm)
	: ObjNodeState(fsm)
	{
	}

	void ObjNodeCategoryDisplayRenderAttributeState::parse(const std::vector<std::string> &tokens, ObjNodeData &data)
	{
		data.reset();
		for(std::size_t i = 0 ; i < tokens.size()-1 ; i++)
		{
			const string &token = tokens[i+1];
			data.strList_.push_back(token);
		}
	}
	void ObjNodeCategoryDisplayRenderAttributeState::update(const ObjNodeData &data, MeshModelBuilder &builder)
	{
		//TODO
	}
	ObjNodeState *ObjNodeCategoryDisplayRenderAttributeState::next(ObjNodeKeyword keyword)
	{
		ObjNodeCategory category = ObjModel::str2category(keyword);

		if(category == ObjNodeCategoryVertexData
		   || category == ObjNodeCategoryDisplayRenderAttribute
		   || category == ObjNodeCategoryElement)
		{
			ObjNodeState &state = fsm().state(keyword);
			return &state;
		}
		SR_ASSERT(!"not valid fsm");
		return NULL;
	}

	ObjNodeMaterialNameState::ObjNodeMaterialNameState(ObjNodeFSM *fsm)
	: ObjNodeCategoryDisplayRenderAttributeState(fsm)
	{
	}
	void ObjNodeMaterialNameState::parse(const std::vector<std::string> &tokens, ObjNodeData &data)
	{
		SR_ASSERT(tokens.size() == 2);
		ObjNodeCategoryDisplayRenderAttributeState::parse(tokens, data);
		data.keyword_ = ObjNodeKeywordMaterialName;
	}

	ObjNodeMaterialLibraryState::ObjNodeMaterialLibraryState(ObjNodeFSM *fsm)
	: ObjNodeCategoryDisplayRenderAttributeState(fsm)
	{
	}
	void ObjNodeMaterialLibraryState::parse(const std::vector<std::string> &tokens, ObjNodeData &data)
	{
		SR_ASSERT(tokens.size() == 2);
		ObjNodeCategoryDisplayRenderAttributeState::parse(tokens, data);
		data.keyword_ = ObjNodeKeywordMaterialLibrary;
	}

	ObjNodeCategoryGroupingState::ObjNodeCategoryGroupingState(ObjNodeFSM *fsm)
	: ObjNodeState(fsm)
	{
	}
	void ObjNodeCategoryGroupingState::parse(const std::vector<std::string> &tokens, ObjNodeData &data)
	{
		data.reset();
		for(std::size_t i = 0 ; i < tokens.size()-1 ; i++)
		{
			const string &token = tokens[i+1];
			data.strList_.push_back(token);
		}
	}
	void ObjNodeCategoryGroupingState::update(const ObjNodeData &data, MeshModelBuilder &builder)
	{
		//TODO
	}
	ObjNodeState *ObjNodeCategoryGroupingState::next(ObjNodeKeyword keyword)
	{
		ObjNodeCategory category = ObjModel::str2category(keyword);

		if(category == ObjNodeCategoryElement
		   || category == ObjNodeCategoryGrouping
		   || category == ObjNodeCategoryDisplayRenderAttribute)
		{
			ObjNodeState &state = fsm().state(keyword);
			return &state;
		}
		SR_ASSERT(!"not valid fsm");
		return NULL;
	}
	ObjNodeGroupNameState::ObjNodeGroupNameState(ObjNodeFSM *fsm)
	: ObjNodeCategoryGroupingState(fsm)
	{
	}
	void ObjNodeGroupNameState::parse(const std::vector<std::string> &tokens, ObjNodeData &data)
	{
		SR_ASSERT(tokens.size() == 2);
		ObjNodeCategoryGroupingState::parse(tokens, data);
		data.keyword_ = ObjNodeKeywordGroupName;
	}
}
